/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */package com.lvneng.processor;

import com.github.zengfr.easymodbus4j.func.AbstractRequest;
import com.github.zengfr.easymodbus4j.func.request.ReadCoilsRequest;
import com.github.zengfr.easymodbus4j.func.request.ReadDiscreteInputsRequest;
import com.github.zengfr.easymodbus4j.func.request.ReadHoldingRegistersRequest;
import com.github.zengfr.easymodbus4j.func.request.ReadInputRegistersRequest;
import com.github.zengfr.easymodbus4j.func.response.ReadCoilsResponse;
import com.github.zengfr.easymodbus4j.func.response.ReadDiscreteInputsResponse;
import com.github.zengfr.easymodbus4j.func.response.ReadHoldingRegistersResponse;
import com.github.zengfr.easymodbus4j.func.response.ReadInputRegistersResponse;
import com.github.zengfr.easymodbus4j.processor.AbstractModbusProcessor;
import com.github.zengfr.easymodbus4j.processor.ModbusMasterResponseProcessor;
import com.github.zengfr.easymodbus4j.protocol.ModbusFunction;
import com.github.zengfr.easymodbus4j.util.ModbusFunctionUtil;
import com.lvneng.core.ModbusConsoleApp;
import io.netty.channel.Channel;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.net.SocketAddress;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @author zengfr QQ:362505707/1163551688 Email:zengfr3000@qq.com
 *         https://github.com/zengfr/easymodbus4j
 */
public class ExampleModbusMasterResponseProcessor extends AbstractModbusProcessor implements ModbusMasterResponseProcessor {
	private static final InternalLogger logger = InternalLoggerFactory.getInstance(ExampleModbusMasterResponseProcessor.class);
	public ExampleModbusMasterResponseProcessor(short transactionIdentifierOffset,boolean isShowFrameDetail) {
		super(transactionIdentifierOffset, isShowFrameDetail);
	}

	/**
	 *
	 * @param channel
	 * @param unitId  //内部去整合的多个slave的从站编号
	 * @param reqFunc
	 * @param respFunc
	 * @return
	 */
	public boolean processResponseFrame(Channel channel, int unitId, AbstractRequest reqFunc, ModbusFunction respFunc) {
        boolean success=this.isRequestResponseMatch(reqFunc, respFunc);
		String socketAddress = channel.remoteAddress().toString();
		if(socketAddress.contains("/")){
			socketAddress = socketAddress.substring(socketAddress.indexOf("/") + 1);
		}
		String key = socketAddress + ":" + unitId;//unitId为内部去整合的多个slave的从站编号
		int externalSlaveId = ModbusConsoleApp.setup.getExternalSlaveId(key);//对外发布的从站编号
		//线圈
		if (respFunc instanceof ReadCoilsResponse) {
			ReadCoilsResponse resp = (ReadCoilsResponse) respFunc;
			short respFunctionCode = resp.getFunctionCode();//功能码
			BitSet coilStatus = resp.getCoilStatus();//线圈状态
			if (reqFunc instanceof ReadCoilsRequest) {
				ReadCoilsRequest req = (ReadCoilsRequest) reqFunc;
				short reqFunctionCode = req.getFunctionCode();//功能码
				int startingAddress = req.getStartingAddress();//起始地址，线圈读取开始地址
				int quantity = req.getQuantityOfCoils();//读取的线圈数量，连续读取数量
				int[] registers = coilStatus.stream().toArray();//只有on的返回，false的不返回
				handleCoilsOrDiscreteInputs(externalSlaveId,reqFunctionCode,startingAddress,quantity,registers);
				logger.debug("接收到响应：ReadCoilsResponse ——> ReadCoilsRequest，startingAddress：" + startingAddress + ",quantity：" + quantity);
				success=true;
			}
		}
		//离散输入
		if (respFunc instanceof ReadDiscreteInputsResponse) {
			ReadDiscreteInputsResponse resp = (ReadDiscreteInputsResponse) respFunc;
			short respFunctionCode = resp.getFunctionCode();//功能码
			BitSet coilStatus = resp.getInputStatus();//线圈状态
			if (reqFunc instanceof ReadDiscreteInputsRequest) {
				ReadDiscreteInputsRequest req = (ReadDiscreteInputsRequest) reqFunc;
				short reqFunctionCode = req.getFunctionCode();//功能码
				int startingAddress = req.getStartingAddress();//起始地址，线圈读取开始地址
				int quantity = req.getQuantityOfCoils();//读取的线圈数量，连续读取数量
				int[] registers = coilStatus.stream().toArray();//只有on的返回，false的不返回
				handleCoilsOrDiscreteInputs(externalSlaveId,reqFunctionCode,startingAddress,quantity,registers);
				logger.debug("接收到响应：ReadDiscreteInputsResponse ——> ReadDiscreteInputsRequest，startingAddress：" + startingAddress + ",quantity：" + quantity);
				success=true;
			}
		}
		//保持寄存器
		if(respFunc instanceof ReadHoldingRegistersResponse){
			ReadHoldingRegistersResponse resp = (ReadHoldingRegistersResponse) respFunc;
			short respFunctionCode = resp.getFunctionCode();//功能码
			int[] registers = resp.getRegisters();
			if (reqFunc instanceof ReadHoldingRegistersRequest) {
				ReadHoldingRegistersRequest req = (ReadHoldingRegistersRequest) reqFunc;
				short reqFunctionCode = req.getFunctionCode();//功能码
				int startingAddress = req.getStartingAddress();//起始地址，寄存器读取开始地址
				int quantity = req.getQuantityOfInputRegisters();//读取的寄存器数量，连续读取数量
				handleHoldingRegistersOrInputRegisters(externalSlaveId,reqFunctionCode,startingAddress,quantity,registers);
				logger.debug("接收到响应：ReadHoldingRegistersResponse ——> ReadHoldingRegistersRequest，startingAddress：" + startingAddress + ",quantity：" + quantity);
				success=true;
			}
		}
		//输入寄存器
		if(respFunc instanceof ReadInputRegistersResponse){
			ReadInputRegistersResponse resp = (ReadInputRegistersResponse) respFunc;
			short respFunctionCode = resp.getFunctionCode();//功能码
			int[] registers = resp.getInputRegisters();
			if (reqFunc instanceof ReadInputRegistersRequest) {
				ReadInputRegistersRequest req = (ReadInputRegistersRequest) reqFunc;
				short reqFunctionCode = req.getFunctionCode();//功能码
				int startingAddress = req.getStartingAddress();//起始地址，寄存器读取开始地址
				int quantity = req.getQuantityOfInputRegisters();//读取的寄存器数量，连续读取数量
				handleHoldingRegistersOrInputRegisters(externalSlaveId,reqFunctionCode,startingAddress,quantity,registers);
				logger.debug("接收到响应：ReadInputRegistersResponse ——> ReadInputRegistersRequest，startingAddress：" + startingAddress + ",quantity：" + quantity);
				success=true;
			}
		}
		if(success){
			logger.debug(String.format("#################################请求成功"+ "，地址：" + key));
		}else{
			logger.debug(String.format("////////////////请求失败"+ "，地址：" + key));
		}

		return success;
	}

	/**
	 * 处理线圈或离散输入
	 * @param releaseToExternalSlaveId
	 * @param reqFunctionCode
	 * @param startingAddress
	 * @param quantity
	 * @param registers
	 */
	private void handleCoilsOrDiscreteInputs(int releaseToExternalSlaveId,short reqFunctionCode,int startingAddress,int quantity,int[] registers){
		IntStream stream = Arrays.stream(registers);//先将int数组转换为数值流
		Stream<Integer> integerStream = stream.boxed();//流中的元素全部装箱，转换为流 ---->int转为Integer
		Integer[] integerRegisters = integerStream.toArray(Integer[]::new);//将流转换为数组
		List<Integer> list = Arrays.asList(integerRegisters);
		for(int i=0;i<quantity;i++){
			int index = startingAddress + i;
			int value = 0;
			if(list.contains(index)){
				value = 1;//返回的那些，设置为1，其余为0
			}
			DataMap.update(releaseToExternalSlaveId,reqFunctionCode,index,value);//
		}
	}

	/**
	 * 处理保持寄存器和输入寄存器
	 * @param releaseToExternalSlaveId
	 * @param reqFunctionCode
	 * @param startingAddress
	 * @param quantity
	 * @param registers
	 */
	private void handleHoldingRegistersOrInputRegisters(int releaseToExternalSlaveId,short reqFunctionCode,int startingAddress,int quantity,int[] registers){
		for(int i=0;i<registers.length;i++){
			DataMap.update(releaseToExternalSlaveId,reqFunctionCode,startingAddress + i,registers[i]);//
		}
	}
}
